# -*- coding:utf-8 -*-
"""
@author: ksf

@since: 2020-04-25 17:48
"""
import types
from collections import defaultdict
from functools import wraps, partial
from threading import Thread
from time import sleep
from typing import List, Dict, Callable
from queue import Queue, Empty

from vcat.core.event import (
    Event, EventType
)
from vcat.core.constant import (
    HandlerLevel,
    WhenHandlerError,
    HandlerCategory
)
from vcat.core.obj import (
    OrderRequest,
    BarData,
    TickData,
    LogData
)


class EventHandler:

    __slots__ = ['handler', 'level']

    def __init__(self,
                 handler: Callable,
                 level: HandlerLevel = HandlerLevel.ON_MAIN_HANDLER):
        self.handler = handler
        self.level = level


class EventBus:

    def __init__(self, interval=1):
        self._interval: int = interval
        self._events_handler: Dict[str, List[Callable]] = defaultdict(list)
        self._general_handlers: List[Callable] = []
        self._guard_algo_dict: Dict[str, 'GuardAlgo'] = {}
        self._evnet_guard_algo_map: Dict[EventType, Dict[str, List['GuardAlgo']]] = defaultdict(lambda: defaultdict(list))
        self._order_checker: List[Callable] = []
        self._queue: Queue = Queue()
        self._running = False
        self._worker: Thread = Thread(target=self._run)
        self._timer: Thread = Thread(target=self._run_timer)          # 时钟
        self._log_count = 0

    def _run(self) -> None:
        """
        Get event from queue and then process it.
        """
        while self._running:
            try:
                event = self._queue.get(block=True, timeout=1)
                self._process(event)
            except Empty:
                pass

    def _process(self, event: Event) -> None:
        """
        First distribute event to those handlers registered listening
        to this type.

        Then distribute event to those general handlers which listens
        to all types.
        """
        if event.event_type == EventType.QUIT:
            self._running = False
            return
        if event.event_type in self._events_handler:
            [handler(event) for handler in self._events_handler[event.event_type]]

        if self._general_handlers:
            [handler(event) for handler in self._general_handlers]

    def _run_timer(self) -> None:
        """
        Sleep by interval second(s) and then generate a timer event.
        """
        while self._running:
            sleep(self._interval)
            event = Event(EventType.TIME, data=None)
            self.put(event)

    def set_interval(self, interval: int):
        self._interval = interval
        del self._timer
        self._timer = Thread(target=self._run_timer)

    def register_handler(self, handler: EventHandler, event_type):
        """注册Event处理"""
        self._events_handler[event_type].append(handler)
        self._events_handler[event_type].sort(key=lambda x: x.level.value)

    def unregister_handler(self, handler: EventHandler, event_type):
        """注册Event处理"""
        self._events_handler[event_type].remove(handler)

    def register_order_checker(self, handler: EventHandler) -> None:
        if handler not in self._general_handlers:
            self._order_checker.append(handler)
            self._order_checker.sort(key=lambda x: x.level.value)

    def unregister_order_checker(self, handler: EventHandler) -> None:
        """
        Unregister an existing general handler function.
        """
        if handler in self._general_handlers:
            self._order_checker.remove(handler)

    def get_event_handlers(self, event_type):
        return self._events_handler[event_type]

    def get_order_checker(self):
        return self._order_checker

    def put(self, event: Event) -> None:
        self._queue.put(event)

    def put_log(self, msg):
        event = Event(event_type=EventType.LOG,
                      data=LogData(msg=msg))
        self.put(event)

    def put_bar(self, bar: BarData):
        event = Event(event_type=EventType.BAR, data=bar)
        self.put(event)

    def put_tick(self, tick: TickData):
        event = Event(event_type=EventType.TICK, data=tick)
        self.put(event)

    def put_stop_single(self):
        event = Event(event_type=EventType.QUIT, data=None)
        self.put(event)

    def register_general(self, handler: EventHandler) -> None:
        if handler not in self._general_handlers:
            self._general_handlers.append(handler)
            self._general_handlers.sort(key=lambda x: x.level.value)

    def unregister_general(self, handler: EventHandler) -> None:
        """
        Unregister an existing general handler function.
        """
        if handler in self._general_handlers:
            self._general_handlers.remove(handler)

    def register_guard_algo(self, algo: 'GuardAlgo'):
        if algo.name in self._guard_algo_dict:
            self.put_log(f'添加 GuardAlgo: {algo.name} 已经存在')
            return
        self._guard_algo_dict[algo.name] = algo
        for ev in algo.event_list:
            for cid in algo.contract_ids:
                self._evnet_guard_algo_map[ev][cid].append(algo)

    def unregister_guard_algo(self, algo_name: str):
        algo = self._guard_algo_dict.get(algo_name)
        if algo is None:
            self.put_log(f'删除 GuardAlgo: {algo_name} 已经存在')
            return
        self._guard_algo_dict.pop(algo_name)
        for ev in algo.event_list:
            for cid in algo.contract_ids:
                try:
                    self._evnet_guard_algo_map[ev][cid].remove(algo)
                except ValueError:
                    pass

    def as_handler(self,
                   category: HandlerCategory,
                   event_type: EventType,
                   level: HandlerLevel,
                   error=WhenHandlerError.LOG):
        def w(method):
            def wrapper(*args, **kwargs):
                try:
                    return method(*args, **kwargs)
                except Exception as e:
                    if error == WhenHandlerError.LOG:
                        self.put_log('[error] AsLeftHandler error:\n' + str(e.with_traceback(None)))
                        raise
                    elif error == WhenHandlerError.IGNORE:
                        pass
                    else:
                        raise

            wrapper.level = level

            if category == HandlerCategory.EVENT:
                print(f'添加 <{event_type.name}> 事件处理对象 <{method}>')
                self.register_handler(wrapper, event_type=event_type)
            elif category == HandlerCategory.GENERAL:
                print(f'添加general处理对象')
                self.register_general(wrapper)
            elif category == HandlerCategory.ORDER_CHECKER:
                print(f'添加order审查处理对象')
                self.register_order_checker(wrapper)
            return wrapper
        return w

    def set_up(self):
        if self._running is True:
            return
        self._running = True
        self._worker.start()
        self._timer.start()

    def tear_down(self):
        self._running = False
        self.join()

    def join(self):
        self._worker.join()
        self._timer.join()


event_bus_left = EventBus()
event_bus_right = EventBus()


class AsLeftHandler:
    """
    注册为输入事件处理函数
    """

    def __init__(self, event_type: EventType, level: HandlerLevel, error=WhenHandlerError.LOG):
        print((self, event_type, level, error))
        self.event_type = event_type
        self.level = level
        self.error = error

    def __call__(self, method):
        @wraps(method)
        def wrapper(*args, **kwargs):

            try:
                return method(*args, **kwargs)
            except Exception as e:
                if self.error == WhenHandlerError.LOG:
                    print('[error] AsLeftHandler error:\n' + str(e.with_traceback(None)))
                elif self.error == WhenHandlerError.IGNORE:
                    pass
                else:
                    raise

        print(f'添加 <{self.event_type.name}> 事件处理对象 <{method}>')
        wrapper.level = self.level
        event_bus_left.register_handler(wrapper, self.event_type)

        return wrapper


class AsOrderReqChecker:
    """
    注册订单发送前检查函数
    """

    def __init__(self, level: HandlerLevel, error=WhenHandlerError.LOG):
        self.event_type = EventType.ORDER_REQ_CHECK
        self.level = level
        self.error = error

    def __call__(self, method):
        def wrapper(_self: EventBus):
            def with_self(*args, **kwargs):
                try:
                    return method(_self, *args, **kwargs)
                except Exception as e:
                    if self.error == WhenHandlerError.LOG:
                        _self.put_log('[error] AsOrderReqChecker error:\n' + str(e.with_traceback(None)))
                    elif self.error == WhenHandlerError.IGNORE:
                        pass
                    else:
                        raise

            e_handler = EventHandler(with_self, level=self.level)
            event_bus_right.register_order_checker(e_handler)
            return with_self
        return wrapper


if __name__ == '__main__':
    from vcat.interface import (
        GuardAlgo
    )